﻿Imports System.Numerics
Imports System.Runtime.CompilerServices
Imports BigDecimals.Math
'Imports BigDecimal.Op_Exts
Partial Class BigDecimal
#Region "Operators"

#Region "Op_Add"

  Shared Operator +(ByVal X As BigDecimal,
                    ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        Select Case X.DecimalPoint.CompareTo(Y.DecimalPoint)
          Case Is = 0 : Return OP_Add(X, Y)
          Case Is < 0 : Return OP_Add(New BigDecimal With {.Digits = BigInteger.Parse(X.Digits.ToString & New String(CChar("0"), CInt(Y.DecimalPoint - X.DecimalPoint))), .DecimalPoint = Y.DecimalPoint}, Y)
          Case Else : Return OP_Add(X, New BigDecimal With {.Digits = BigInteger.Parse(Y.Digits.ToString & New String(CChar("0"), CInt(X.DecimalPoint - Y.DecimalPoint))), .DecimalPoint = X.DecimalPoint})
        End Select
      Else
        Throw New ArgumentNullException("+: Y is null")
      End If
    Else
      Throw New ArgumentNullException("+: X is null")
    End If

  End Operator
  Protected Friend Shared Function OP_Add(ByVal X As BigDecimal,
                                          ByVal Y As BigDecimal) As BigDecimal
    Return New BigDecimal With {.DecimalPoint = X.DecimalPoint, .Digits = X.Digits + Y.Digits}
  End Function
#End Region

#Region "Op_Sub"
  Shared Operator -(ByVal X As BigDecimal,
                   ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        Select Case X.DecimalPoint.CompareTo(Y.DecimalPoint)
          Case Is = 0 : Return OP_Sub(X, Y)
          Case Is < 0 : Return OP_Sub(X.AlignedTo(Y), Y)
          Case Else : Return OP_Sub(X, Y.AlignedTo(X))
        End Select
      Else
        Throw New ArgumentNullException("-: Y is null")
      End If
    Else
      Throw New ArgumentNullException("-: X is null")
    End If
  End Operator

  Protected Friend Shared Function OP_Sub(ByVal X As BigDecimal,
                                         ByVal Y As BigDecimal) As BigDecimal
    Return New BigDecimal With {.DecimalPoint = X.DecimalPoint, .Digits = X.Digits - Y.Digits}
  End Function

  Shared Operator -(ByVal X As BigDecimal) As BigDecimal
    If X.IsntNull Then
      'Return Zero - X
      Return New BigDecimal(BigInteger.Negate(X.Digits), X.DecimalPoint)
    Else
      Throw New ArgumentNullException("-: X is null")
    End If
  End Operator

#End Region

#Region "Power"
  Shared Operator ^(ByVal X As BigDecimal,
                ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        If Y.IsInteger Then
          Return X.Pow(Y)
        Else
          Throw New NotSupportedException("Pow: Real Powers aren't supported")
        End If

      Else
        Throw New ArgumentNullException("*: Y is null")
      End If
    Else
      Throw New ArgumentNullException("*: X is null")
    End If
  End Operator
#End Region

#Region "Op_Mul"
  Shared Operator *(ByVal X As BigDecimal,
                  ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        Select Case X.DecimalPoint.CompareTo(Y.DecimalPoint)
          Case Is = 0 : Return OP_Mul(X, Y)
          Case Is < 0 : Return OP_Mul(X.AlignedTo(Y), Y)
          Case Else : Return OP_Mul(X, Y.AlignedTo(X))
        End Select
      Else
        Throw New ArgumentNullException("*: Y is null")
      End If
    Else
      Throw New ArgumentNullException("*: X is null")
    End If
  End Operator
  Protected Friend Shared Function OP_Mul(ByVal X As BigDecimal,
                                           ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull And Y.IsntNull Then
      Return New BigDecimal With {.DecimalPoint = X.DecimalPoint + Y.DecimalPoint, .Digits = X.Digits * Y.Digits}
    End If

    Throw New ArgumentNullException
  End Function
#End Region


  Public Shared Function TryParse(ByVal S As String, ByRef Result As BigDecimal) As Boolean
    Dim MatchesOfRegEx = RegexForDecimal.Matches(S)
    ' Does the string match the regex for a decimal?
    If MatchesOfRegEx.Count = 0 Then
      ' No, so passback nothing and return False
      Result = Nothing
      Return False
    Else
      ' Yes, the string matches the regex.
      Dim sb As New System.Text.StringBuilder()
      Dim si = MatchesOfRegEx(0).Groups("Sign").Captures
      ' Does the number have a sign?
      If si.Count = 1 Then sb.Append(si(0).Value)
      Dim ss = MatchesOfRegEx(0).Groups("INTEGER").Captures
      ' Does the digit have a integer part?
      If ss.Count = 1 Then
        ' Append Integer Part to 
        sb.Append(ss(0).Value)
      Else
        ' Not default to Zero
        sb.Append("0")
      End If
      Dim ss2 = MatchesOfRegEx(0).Groups("FLOAT").Captures
      Dim dp As UInteger = 0
      If ss2.Count > 0 Then
        dp = CUInt(ss2(0).Value.Length)
        sb.Append(ss2(0).Value)
      End If
      Result = BigDecimal.CreateNew(BigInteger.Parse(sb.ToString), dp)
      Return True
    End If


  End Function

  Shared Operator /(ByVal X As BigDecimal, ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        Return PreDiv(X, Y) 'Division(X, Y) 
      Else
        Throw New ArgumentNullException("/: Y is null")
      End If
    Else
      Throw New ArgumentNullException("/: X is null")
    End If
  End Operator

  Private Shared Function PreDiv(ByVal x As BigDecimal, ByVal y As BigDecimal) As BigDecimal
    If y = BigDecimal.Zero Then Throw New DivideByZeroException
    If x = BigDecimal.Zero Then Return BigDecimal.Zero
    If x = y Then Return DirectCast(x.Clone, BigDecimal)
    If y = BigDecimal.One Then Return DirectCast(x.Clone, BigDecimal)
    Return Division(x, y)
  End Function
  Private Shared Function DivRem(ByVal x As BigDecimal, ByVal y As BigDecimal, ByRef remain As BigDecimal) As BigDecimal
    Dim Modulas As New BigDecimal(0)
    While x >= y AndAlso x >= Zero()
      Modulas.Increment()
      x -= y
    End While
    If x < Zero Then
      x += y
      Modulas.Decrement()
    End If
    remain = x
    Return Modulas
  End Function



  Shared Operator Mod(ByVal X As BigDecimal, ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        Dim tmp1 As BigDecimal = Nothing
        DivRem(X, Y, tmp1)
        Return tmp1
      Else
        Throw New ArgumentNullException("Mod: Y is null")
      End If
    Else
      Throw New ArgumentNullException("Mod: X is null")
    End If
  End Operator

  Shared Operator \(ByVal X As BigDecimal, ByVal Y As BigDecimal) As BigDecimal
    If X.IsntNull Then
      If Y.IsntNull Then
        Return DivRem(X, Y, Nothing)
      Else
        Throw New ArgumentNullException("Mod: Y is null")
      End If
    Else
      Throw New ArgumentNullException("Mod: X is null")
    End If
  End Operator


#End Region


End Class

